package org.czl.example.mybatis;

import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert;
import com.baomidou.mybatisplus.generator.config.converts.select.BranchBuilder;
import com.baomidou.mybatisplus.generator.config.converts.select.Selector;
import com.baomidou.mybatisplus.generator.config.querys.MySqlQuery;
import com.baomidou.mybatisplus.generator.config.rules.IColumnType;
import com.baomidou.mybatisplus.generator.keywords.MySqlKeyWordsHandler;
import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.util.CollectionUtils;

import java.util.Collections;
import java.util.List;

import static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.*;

/**
 * @author CaiZhuliang
 * @date 2024/5/25
 */
public class CodeGenerator {
    @SuppressWarnings("deprecation")
    public static void main(String[] args) {
        String url = args[0];
        String username = args[1];
        String password = args[2];
        DataSourceConfig dataSourceConfig = buildDataSourceConfig(url, username, password);
        List<InnerConfig> innerConfigList = getInnerConfigs();
        for (InnerConfig innerConfig : innerConfigList) {
            InnerStrategyConfig strategyConfig = innerConfig.getStrategyConfig();
            // 代码生成器
            AutoGenerator generator = new AutoGenerator(dataSourceConfig);
            generator.global(buildGlobalConfig(innerConfig))
                    .packageInfo(buildPackageInfo(innerConfig.getPackageConfig(), strategyConfig))
                    .strategy(buildStrategyConfig(strategyConfig))
                    .template(buildTemplate(strategyConfig))
                    .execute();
        }
    }

    @SuppressWarnings("deprecation")
    private static TemplateConfig buildTemplate(InnerStrategyConfig strategyConfig) {
        return new TemplateConfig.Builder()
                // 不生成哪些模板代码
                .disable(strategyConfig.getDisableTemplateTypeList().toArray(new TemplateType[0]))
                .build();
    }

    private static StrategyConfig buildStrategyConfig(InnerStrategyConfig strategyConfig) {
        List<TemplateType> disableTemplateTypeList = strategyConfig.getDisableTemplateTypeList();
        if (CollectionUtils.isEmpty(disableTemplateTypeList)) {
            return new StrategyConfig.Builder()
                    .addInclude(strategyConfig.getInclude())
                    .addTablePrefix(strategyConfig.getTablePrefixList())
                    .entityBuilder()
                    .formatFileName(strategyConfig.getFormatFileName())
                    .disableSerialVersionUID()
                    .enableLombok()
                    .build();
        }
        if (disableTemplateTypeList.contains(TemplateType.SERVICE) ||
                disableTemplateTypeList.contains(TemplateType.SERVICE_IMPL)) {
            return new StrategyConfig.Builder()
                    .addInclude(strategyConfig.getInclude())
                    .addTablePrefix(strategyConfig.getTablePrefixList())
                    .controllerBuilder()
                    .disable()
                    .serviceBuilder()
                    .disable()
                    .entityBuilder()
                    .formatFileName(strategyConfig.getFormatFileName())
                    .disableSerialVersionUID()
                    .enableLombok()
                    .enableFileOverride()
                    .mapperBuilder()
                    .enableFileOverride()
                    .build();
        }
        return new StrategyConfig.Builder()
                .addInclude(strategyConfig.getInclude())
                .addTablePrefix(strategyConfig.getTablePrefixList())
                .entityBuilder()
                .disable()
                .mapperBuilder()
                .disable()
                .controllerBuilder()
                .disable()
                .serviceBuilder()
                .enableFileOverride()
                .build();
    }

    private static PackageConfig buildPackageInfo(
            InnerPackageConfig packageConfig,
            InnerStrategyConfig strategyConfig
    ) {
        List<TemplateType> disableTemplateTypeList = strategyConfig.getDisableTemplateTypeList();
        if (CollectionUtils.isEmpty(disableTemplateTypeList)) {
            return new PackageConfig.Builder()
                    .parent(packageConfig.getParent())
                    .service(packageConfig.getService())
                    .serviceImpl(packageConfig.getServiceImpl())
                    .entity(packageConfig.getEntity())
                    .mapper(packageConfig.getMapper())
                    .pathInfo(Collections.singletonMap(OutputFile.xml, packageConfig.getXml()))
                    .build();
        }
        if (disableTemplateTypeList.contains(TemplateType.SERVICE) ||
                disableTemplateTypeList.contains(TemplateType.SERVICE_IMPL)) {
            return new PackageConfig.Builder()
                    .parent(packageConfig.getParent())
                    .entity(packageConfig.getEntity())
                    .mapper(packageConfig.getMapper())
                    .pathInfo(Collections.singletonMap(OutputFile.xml, packageConfig.getXml()))
                    .build();
        }
        return new PackageConfig.Builder()
                .parent(packageConfig.getParent())
                .service(packageConfig.getService())
                .serviceImpl(packageConfig.getServiceImpl())
                .build();
    }

    private static GlobalConfig buildGlobalConfig(InnerConfig innerConfig) {
        return new GlobalConfig.Builder()
                .outputDir(innerConfig.getOutputDir())
                .author("CaiZhuliang")
                // 不自动打开目录
                .disableOpenDir()
                .build();
    }

    private static DataSourceConfig buildDataSourceConfig(String url, String username, String password) {
        return new DataSourceConfig.Builder(url, username, password)
                .dbQuery(new MySqlQuery())
                .typeConvert(new MyTypeConvert())
                .keyWordsHandler(new MySqlKeyWordsHandler())
                .build();
    }

    private static List<InnerConfig> getInnerConfigs() {
        InnerConfig daoConfig = InnerConfig.builder()
                .outputDir("./mybatis-generate-dao/src/main/java")
                .packageConfig(InnerPackageConfig.builder()
                        .parent("org.czl.example.mybatis")
                        .entity("entity")
                        .mapper("mapper")
                        .xml("./mybatis-generate-dao/src/main/resources/mapper")
                        .build())
                .strategyConfig(InnerStrategyConfig.builder()
                        .include("t_water_num,t_music,t_water_generator")
                        .tablePrefixList(Lists.newArrayList("t_"))
                        .formatFileName("%sEntity")
                        .disableTemplateTypeList(Lists.newArrayList(TemplateType.CONTROLLER, TemplateType.SERVICE,
                                TemplateType.SERVICE_IMPL))
                        .build())
                .build();
        InnerConfig serviceConfig = InnerConfig.builder()
                .outputDir("./mybatis-generate-service/src/main/java")
                .packageConfig(InnerPackageConfig.builder()
                        .parent("org.czl.example.mybatis")
                        .service("service")
                        .serviceImpl("service.impl")
                        .build())
                .strategyConfig(InnerStrategyConfig.builder()
                        .include("t_water_num,t_music,t_water_generator")
                        .tablePrefixList(Lists.newArrayList("t_"))
                        .formatFileName("%sEntity")
                        .disableTemplateTypeList(Lists.newArrayList(TemplateType.CONTROLLER, TemplateType.MAPPER,
                                TemplateType.XML, TemplateType.ENTITY))
                        .build())
                .build();
        return Lists.newArrayList(daoConfig, serviceConfig);
    }

    public static class MyTypeConvert extends MySqlTypeConvert {
        @Override
        public IColumnType processTypeConvert(GlobalConfig config, String fieldType) {
            return MyTypeConverts.use(fieldType)
                    .test(MyTypeConverts.containsAny("char", "text", "json", "enum").then(STRING))
                    .test(MyTypeConverts.contains("bigint").then(LONG))
                    .test(MyTypeConverts.containsAny("tinyint(1)").then(INTEGER))
                    .test(MyTypeConverts.containsAny("bit(1)").then(BOOLEAN))
                    .test(MyTypeConverts.contains("bit").then(BYTE))
                    .test(MyTypeConverts.contains("int").then(INTEGER))
                    .test(MyTypeConverts.contains("decimal").then(BIG_DECIMAL))
                    .test(MyTypeConverts.contains("clob").then(CLOB))
                    .test(MyTypeConverts.contains("blob").then(BLOB))
                    .test(MyTypeConverts.contains("binary").then(BYTE_ARRAY))
                    .test(MyTypeConverts.contains("float").then(FLOAT))
                    .test(MyTypeConverts.contains("double").then(DOUBLE))
                    .test(MyTypeConverts.containsAny("date", "time", "year").then(t -> toDateType(config, t)))
                    .or(STRING);
        }
    }

    public static class MyTypeConverts {
        public static Selector<String, IColumnType> use(String param) {
            return new Selector<>(param.toLowerCase());
        }

        public static BranchBuilder<String, IColumnType> containsAny(CharSequence... values) {
            return BranchBuilder.of(s -> {
                for (CharSequence value : values) {
                    if (s.contains(value)) {
                        return true;
                    }
                }
                return false;
            });
        }

        public static BranchBuilder<String, IColumnType> contains(CharSequence value) {
            return BranchBuilder.of(s -> s.contains(value));
        }
    }

    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    static class InnerConfig {
        private String outputDir;
        private InnerPackageConfig packageConfig;
        private InnerStrategyConfig strategyConfig;
    }

    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    static class InnerPackageConfig {
        private String parent;
        private String entity;
        private String service;
        private String serviceImpl;
        private String mapper;
        private String xml;
    }

    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    static class InnerStrategyConfig {
        private String include;
        private List<String> tablePrefixList;
        private String formatFileName;
        private List<TemplateType> disableTemplateTypeList = Lists.newArrayList();
    }
}